#!/usr/bin/env python3
# NOTE: PYTHONUNBUFFERED is set in the entrypoint for unbuffered output
#
# Allows users to operate on the tags in `builds.json`
#
# Examples:
#
#   $ coreos-assembler tag update --tag smoketested --build 47.152
#   $ coreos-assembler tag delete --tag beta
#   $ coreos-assembler tag list
#
import argparse
import os
import sys

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from cosalib.builds import Builds
from cosalib.cmdlib import fatal, rfc3339_time


def main():
    """Main entry point"""

    args = parse_args()
    args.func(args)


def parse_args():
    """Parse args and dispatch"""

    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(dest="cmd", title="Tag actions")
    subparsers.required = True

    delete = subparsers.add_parser("delete",
                                   help="Delete a tag from the metadata")
    delete.add_argument("--tag", help="Tag to delete from metadata")
    delete.add_argument("--all", help="Delete all tags",
                        action="store_true")
    delete.set_defaults(func=cmd_delete)

    list_tags = subparsers.add_parser("list", help="List available tags in "
                                                   "the metadata")
    list_tags.set_defaults(func=cmd_list)

    update = subparsers.add_parser("update", help="Update existing tag or "
                                                  "create a new tag")
    update.add_argument("--tag", help="Tag to be updated", required=True)
    update.add_argument("--description", help="Message to associate with tag")
    update.add_argument("--build", help="Build to update tag with")
    update.add_argument("--force", help="Force the update of a tag",
                        action="store_true")
    update.set_defaults(func=cmd_update)

    return parser.parse_args()


def available_tags(build_data):
    """Returns the available tag names from build metadata"""

    return [t.get("name") for t in build_data.get("tags", {})]


def cmd_list(args):
    """List available tags in build metadata"""

    builds = Builds()
    build_data = builds.raw()
    avail_tags = available_tags(build_data)
    if not avail_tags:
        print("No tags found")
        return

    for tag in build_data["tags"]:
        print(f"name: {tag['name']}\n"
              f"created: {tag['created']}\n"
              f"target: {tag['target']}\n", end='')
        if 'description' in tag:
            print(f"description:\n\t{tag['description']}\n", end='')
        print()


def cmd_update(args):
    """Create or update a tag to new build ID"""

    builds = Builds()
    if args.build is None:
        args.build = builds.get_latest()
    build_data = builds.raw()

    avail_tags = available_tags(build_data)

    if "tags" not in build_data:
        build_data["tags"] = []

    # if the build doesn't exist, check for the force flag and bail out
    # if it is not used.
    if not builds.has(args.build):
        if not args.force:
            fatal("Cannot operate on a tag with a build that does not exist")
        print(f"INFO: Operating on a tag ({args.tag}) with a build "
              f"({args.build}) that does not exist")

    # we've got a build (either forced or not), so time to make/update a tag
    created = rfc3339_time()
    new_tag = {
        "name": args.tag,
        "created": created,
        "target": args.build,
    }
    if args.description:
        new_tag['description'] = args.description
    if args.tag not in avail_tags:
        build_data["tags"].append(new_tag)
    else:
        build_data["tags"] = [new_tag if t.get("name") == args.tag
                              else t for t in build_data["tags"]]
    builds.flush()


def cmd_delete(args):
    """Delete a tag from build metadata"""

    # To delete a tag, iterate through existing tags list, and
    # drop the entry we want
    builds = Builds()
    build_data = builds.raw()

    if args.all:
        if "tags" in build_data:
            del build_data["tags"]
    elif args.tag:
        avail_tags = available_tags(build_data)
        if args.tag not in avail_tags:
            fatal("Cannot delete a tag that does not exist")
        build_data["tags"] = [t for t in build_data["tags"]
                              if t["name"] != args.tag]
    else:
        fatal("Either --tag or --all required")

    builds.flush()


if __name__ == "__main__":
    sys.exit(main())
